package com.neuronrobotics.replicator.gui.preview.android;

import java.util.Arrays;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Vibrator;
import android.util.Log;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;

import com.neuronrobotics.replicator.gui.preview.controller.GeneralCameraController;
import com.neuronrobotics.replicator.gui.preview.controller.GeneralSTLTransformHelper;
import com.neuronrobotics.replicator.gui.preview.controller.STLPreviewMouseController;
import com.neuronrobotics.replicator.gui.preview.model.STLWorkspaceModel;
import com.neuronrobotics.replicator.gui.stl.GeneralTransform3D.MatrixOrder;
import com.neuronrobotics.replicator.gui.stl.STLFace;
import com.neuronrobotics.replicator.gui.stl.STLObject;
import com.neuronrobotics.replicator.gui.stl.STLWorkspaceObject;

public class STLPreviewTouchController extends TouchDelegate implements
		STLPreviewMouseController, OnLongClickListener, OnClickListener, OnTouchListener {

	private MouseControlMode currentMode = MouseControlMode.CAMERA_ROTATE;

	
	private float mPreviousX;
	private float mPreviousY;
	private STLPreviewViewAndroid theView;
	private STLWorkspaceModel theModel;
	private GeneralSTLTransformHelper stlController;
	private GeneralCameraController theCameraController;
	
	private PickingRay mostRecentPickRay = new PickingRay();
	private STLObject mostRecentPick = null;
	private STLFace mostRecentPickFace = null;
	
	private float downX = Float.NaN, downY = Float.NaN,
			totalDisplacementX = Float.NaN, totalDisplacementY = Float.NaN,
			totalDistanceX=Float.NaN,totalDistanceY=Float.NaN;
	
	private float[] currentPickTransform = new float[16];

	private float[] center = new float[]{0,0,0};
	
	public STLPreviewTouchController(STLPreviewViewAndroid v, STLWorkspaceModel model, GeneralSTLTransformHelper stlCon, GeneralCameraController camCon) {
		super(new Rect(), v);
		this.stlController=stlCon;
		this.theCameraController=camCon;
		this.theModel = model;
		this.theView = v;
		this.theView.setHapticFeedbackEnabled(false);
		v.setOnTouchListener(this);
		
		v.setLongClickable(true);
		v.setOnClickListener(this);
		v.setOnLongClickListener(this);
	}

	@Override
	public void setCurrentMouseControlMode(
			MouseControlMode currentSelectedMouseMode) {
		this.currentMode = currentSelectedMouseMode;
	}
	
	public MouseControlMode getCurrentMouseControlMode(){
		return this.currentMode;
	}
	
		
	private String actionAsString(MotionEvent e){
		if(e.getAction()==MotionEvent.ACTION_DOWN){
			return "Action_Down";
		} else if(e.getAction()==MotionEvent.ACTION_UP){
			return "Action_Up";
		}
		
		return "Kneether";
	}	
	
	private void updateModel(boolean render){
		
		if(currentMode==MouseControlMode.CAMERA_ROTATE||mostRecentPick==null) return;
		
		stlController.getCurrentTransform(currentPickTransform,MatrixOrder.ROW_MAJOR);
		theModel.setTransformMatrix(mostRecentPick, currentPickTransform,MatrixOrder.ROW_MAJOR);
		//TODO
		float[] maxes = new float[3];
		theModel.getCurrMins(mostRecentPick, maxes);
		Log.d("TranTest",Arrays.toString(maxes));
		if(render) this.theView.requestRender();
		
	}

	@Override
	public void setModel(STLWorkspaceModel model) {
		this.theModel = model;
	}

	@Override
	public boolean onLongClick(View v) {
		Log.d("LC","Long click d-tected");	
		Log.d("LC","Downx: "+downX+" DownY: "+downY);
		Log.d("LC", "Last known X: "+mPreviousX+" LKY"+mPreviousY);
		Log.d("LC","totX: "+totalDisplacementX+" totY"+totalDisplacementY);
		
		if(totalDisplacementX<1&&totalDisplacementY<1){
			Vibrator vibe = (Vibrator)this.theView.getContext().getSystemService(Activity.VIBRATOR_SERVICE);
	        vibe.vibrate(50);			
			return false; //this allows the long click to fire a context menu
		}
		return true;//this prevents context menu from firing
	}

	@Override
	public void onClick(View v) {
		Log.d("LC","Reg'lar ole click");		
	}
	
	@Override
	public boolean onTouch(View arg0, MotionEvent e) {	
				
		//Log.d("LC","Touch even detected");		
		Log.d("Action",actionAsString(e));
		
		float x = e.getX();
		float y = e.getY();
						
		float xDist = x - mPreviousX, yDist = y - mPreviousY;
		
		//TODO
		if(currentMode.isClickMode()){
			if (currentMode == MouseControlMode.CAMERA_ROTATE) {
				if (mostRecentPick != null)
					theView.clearPick();
				mostRecentPick = null;
			} else if (theModel != null) {
				theView.setPickLocation(x, y);
				mostRecentPick = theView.getPick(mostRecentPickRay);
			}
			return false;
		}
								
		switch (e.getAction()) {
		case MotionEvent.ACTION_UP:
			this.theView.setLongClickable(true);
			theView.clearPick();
			mostRecentPick = null;
			mostRecentPickFace = null;
			downX = Float.NaN;
			downY = Float.NaN;
			totalDisplacementX = Float.NaN;
			totalDisplacementY = Float.NaN;
			totalDistanceX = Float.NaN;
			totalDistanceY = Float.NaN;
			break;
		case MotionEvent.ACTION_DOWN:
			downX = x;
			downY = y;
			totalDisplacementX = 0;
			totalDisplacementY = 0;
			totalDistanceX = 0;
			totalDistanceY = 0;
			this.theView.setLongClickable(true);
			if (currentMode == MouseControlMode.CAMERA_ROTATE) {
				if (mostRecentPick != null) theView.clearPick();
				mostRecentPick = null;
				mostRecentPickFace = null;
			} else if (theModel != null) {
				theView.setPickLocation(x, y);
				mostRecentPick = theView.getPick(mostRecentPickRay);
				
			}
			break;
		case MotionEvent.ACTION_MOVE:
			totalDisplacementX+=Math.abs(xDist);
			totalDisplacementY+=Math.abs(yDist);
			totalDistanceX+= xDist;
			totalDistanceY+= yDist;
			if((totalDisplacementX>1)||(totalDisplacementY>1)) this.theView.setLongClickable(false);
			float[] maxes = new float[3], mins = new float[3];
			if (mostRecentPick != null) {
				
				theModel.getCurrMaxes(mostRecentPick, maxes);
				theModel.getCurrMins(mostRecentPick, mins);

				float[] centerF = new float[3];
				mostRecentPick.getCenter().get(centerF);
				center = new float[] { centerF[0], centerF[1], centerF[2] };
				
							
				theModel.getTransformMatrix(mostRecentPick,
						currentPickTransform, MatrixOrder.ROW_MAJOR);
				stlController.setCurrentTransform(currentPickTransform,
						MatrixOrder.ROW_MAJOR);
				
				mostRecentPickFace = mostRecentPickRay.getPickFace(mostRecentPick, currentPickTransform);
				
			}
			
			ModeParameters params = new ModeParameters();
			params.transformHelper = stlController;
			params.theCameraController = theCameraController;
			params.xDown = mPreviousX;
			params.yDown = mPreviousY;
			params.xFinal = x;
			params.yFinal = y;
			
			params.xDist = xDist;
			params.yDist = yDist;
			params.center = center;
			params.mins = mins;
			params.maxes = maxes;
			
			STLWorkspaceObject theWSO = theModel.getWorkspaceObject();
			if(theWSO!=null){
				
				params.replaceOnSurface = true;
				params.surfacyY = theWSO.getSurfaceY();
			}
			
			params.maintainCenterJustXZ = true;
			
			
			
			if(mostRecentPickFace!=null) mostRecentPickFace.getNormal(params.orientingVector, 0);
								
			if(this.currentMode!=MouseControlMode.SCALE) params.maintainCenter = true;
			else params.maintainCenter = false;
			theView.getCameraData(params.cameraPos, params.cameraLook);
			params.correctForCamera = true;
			this.currentMode.mouseDragged(params);
			this.updateModel(false);
			//TODO might be better to specify only the model just moved?
			this.theView.getTheController().placeAllObjectsOnWorkspace();
			this.theView.requestRender();
			break;
		default:
			break;
		}
		
		mPreviousX = x;
		mPreviousY = y;				
		return false;
	}
		
}
